home *** CD-ROM | disk | FTP | other *** search
/ PC-X 1997 October / pcx14_9710.iso / swag / delphi.swg / 0081_Using TStream-TWrite.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1995-11-24  |  13.7 KB  |  498 lines

  1.  
  2. For those interested in storing components onto a stream, take a look
  3. at TWriter.WriteRootComponent and TWriter.WriteComponent.  Then check
  4. out the TReader.Read... counterparts.  Unfortunately, there appears
  5. to be no documentation showing HOW to use these methods properly.
  6. The docs keep mentioning the "root" component, but never clearly
  7. explain what it is or how you are suppose to use the root property
  8. to store your components.
  9.  
  10. If you are interested in writing objects to the stream that are
  11. not components, I recommend doing something like the follow:
  12.  
  13. type
  14.   TMyObject = class(TObject)
  15.     ...
  16.   protected
  17.     procedure SaveToStream(writer : TWriter); virtual; 
  18.     procedure LoadFromStream(writer : TWriter); virtual;
  19.     ...
  20.   end;
  21.  
  22. procedure TMyObject.SaveToStream(writer : TWriter);
  23. begin
  24.   with writer do begin
  25.     WriteListBegin;
  26.     {- write object state -}
  27.     WriteListEnd;
  28.   end;
  29. end;
  30.  
  31. procedure TMyObject.LoadFromStream(reader : TReader);
  32. begin
  33.   with reader do begin
  34.     ReadListBegin;
  35.     while not EndOfList do begin
  36.       {- read object state -}
  37.     end;
  38.     ReadListEnd;
  39.   end;
  40. end;
  41.  
  42. Somewhere in the initialization section of the unit in which this
  43. object is declared, call RegisterObject('TMyObject').  (See
  44. RegisterObject() below.)
  45.  
  46. In the main program, where you specify the file to read/write,
  47. you can do something like this:
  48.  
  49. var
  50.   RegisteredObjects : TStringList;
  51.  
  52. procedure RegisterObject(cname : string; ctype : TClass);
  53. begin
  54.   RegisteredObjects.AddObject(cname, ctype);
  55. end;
  56.  
  57. procedure GetObject(cname : string) : TClass;
  58. var
  59.   i : integer;
  60. begin
  61.   i := RegisteredObjects.IndexOf(cname);
  62.   if i > -1 then
  63.     Result := TClass(RegisteredObjects.Objects[i])
  64.   else
  65.     Result := nil;
  66. end;
  67.  
  68. procedure SaveFile(const filename : string; objlist : TList);
  69. var
  70.   stream : TFileStream;
  71.   writer : TWriter;
  72.   i      : integer;
  73. begin
  74.   stream := TFileStream.Create(filename, fmCreate or fmOpenWrite);
  75.   try
  76.     writer := TWriter.Create(stream, $ff);
  77.     try
  78.       with writer do begin
  79.         WriteSignature;     {marker to indicate a Delphi filer object file.}
  80.         WriteListBegin;     {outer list marker}
  81.         for i := 0 to objlist.Count - 1 do begin
  82.           WriteListBegin;   {object marker}
  83.           WriteString(TMyObject(objlist[i]).ClassName);
  84.           TMyObject(objlist[i]).SaveToStream(writer);
  85.           WriteListEnd;     {object marker}
  86.         end;
  87.         WriteListEnd;       {outer list marker}
  88.       end;
  89.     finally
  90.       writer.Free;
  91.     end;
  92.   finally
  93.     stream.Free;
  94.   end;
  95. end;
  96.  
  97. procedure OpenFile(const filename : string; objlist : TList);
  98. var
  99.   stream : TFileStream;
  100.   writer : TWriter;
  101.   cname  : string;  {class name}
  102.   ctype  : TClass;  {class type}
  103.   obj    : TObject;
  104. begin
  105.   stream := TFileStream.Create(filename, fmOpenRead);
  106.   try
  107.     reader := TReader.Create(stream, $ff);
  108.     try
  109.       with reader do begin
  110.         ReadSignature;     {check Delphi filer object signature.}
  111.         ReadListBegin;     {outer list marker}
  112.         while not EndOfList do begin
  113.           ReadListBegin;   {object marker}
  114.           while not EndOfList do begin
  115.             cname := ReadString;
  116.             ctype := GetObjectClass(cname);
  117.             obj := TObject(TObjectClass(ctype).Create;
  118.             try
  119.               obj.LoadFromStream(reader);
  120.             except
  121.               obj.Free;
  122.               raise;
  123.             end;
  124.             objlist.Add(obj);
  125.           end;
  126.           ReadListEnd;     {object marker}
  127.         end;
  128.         ReadListEnd;       {outer list marker}
  129.       end;
  130.     finally
  131.       reader.Free;
  132.     end;
  133.   finally
  134.     stream.Free;
  135.   end;
  136. end;
  137.  
  138.  
  139. Well, I don't know how far this will get you.  I haven't tested ANY
  140. of this code, so who knows if it could ever possibly work.  The most
  141. doubtful part is the whole dynamic instantiation taking place in
  142. LoadFromStream().  Delphi provides a bunch of great functions for
  143. registering TPersistent descendants and getting their class types
  144. from their class names, etc.:  RegisterClass(), FindFieldClass(),
  145. FindClass(), GetClass(), etc.  (They use it for loading components
  146. off of streams....no surprise there.)  However, if your objects
  147. are not TPersistent descendants (and there's no reason they should
  148. be), then you're basically out of luck (read: "you get to write
  149. your own RegisteredClass()").
  150.  
  151. So, give this a try if you're feeling daring.  Just don't come running
  152. after me with a shotgun complaining about little voices in your heads
  153. if you do.  I suspect the above code will need a lot of polish before
  154. it does what is expected of it...  Nonetheless, I hope you find it
  155. interesting, if nothing else.
  156.  
  157. ----------------------------------------------------------------------
  158.  
  159. Well, shoot.  After a bit of research and review, I came to realize just
  160. how unnecessary all of this work with trying to store plain objects
  161. on a stream is.  When I wrote up the TStream2 message back in May
  162. (only shortly after Delphi came out), I did not have a good understanding
  163. of the VCL class heirarchy.
  164.  
  165. Here's a quote from the Component Writer's Guide (TPersistent):
  166.  
  167. The TPersistent object is the abstract base object for all objects
  168. stored and loaded on Delphi stream objects. In addition to the methods
  169. it inherits from its ancestor, TObject, TPersistent defines three new
  170. methods: AssignTo and DefineProperties, which are protected, and Assign,
  171. which is public.
  172.  
  173. The GetClass() and RegisterClass() functions work for TPersistent objects.
  174.  
  175. So, after being in the dark for so long, I sat down and just took a
  176. good long look at TPersistent and other related matters.  Then, after
  177. looking back at what you wanted to do, I wrote up a little program
  178. and tested it out to make sure it actually worked.  Below is the result.
  179.  
  180. Below I have included two units: Unit1, which is a form definition, and
  181. Unit2, which contains the TPlayer and TObjectList classes.  The form
  182. (Unit1) has for buttons labelled "Create," "Save," "Load," and "Exit."
  183.  
  184.         Create -- create 5 TPlayers and add them to the object list
  185.         Save   -- save the object list to a file
  186.         Load   -- load the object list from a file
  187.         Exit   -- free the object list and exit
  188.  
  189. In Unit2, the TPlayer object is declared as a TPersistent descendant
  190. and is given two methods:  ReadData() and WriteData().
  191.  
  192.         ReadData()  -- read property data with given TReader object
  193.         WriteData() -- write property data with given TWriter object
  194.  
  195. The TPlayer class is registered with a call to RegisterClass in the
  196. initialization section of Unit2 when the program first begins.
  197.  
  198. Also in Unit2, the TObjectList class is declared as a TList and
  199. given the methods Clear(), SaveToStream(), LoadFromStream(),
  200. SaveToFile(), and LoadFromFile().  The SaveToFile() and LoadFromFile()
  201. just create a TFileStream object and then pass it to the corresponding
  202. SaveToStream()/LoadFromStream() method, which do the actually accessing
  203. via the TFiler objects (TWriter & TReader).  A destructor was also
  204. added to TObjectList to ensure that it frees the items in the list
  205. when it is destroyed.
  206.  
  207. I think it would be a good idea to go back into the TObjectList and
  208. add a new kind of Items property that is specifically typed as
  209. TPersistent or TObject instead of just Pointer since many of the
  210. operations we perform on its Items property could cause problems
  211. if a non-object were accidentally stored in the list.  It would also
  212. reduce the need for all of the extra type casting.
  213.  
  214. Anyhow, here are the two units that worked for me.  Let me know what you
  215. think.
  216.  
  217. --Mark Johnson
  218.  
  219. --------------------------------- UNIT1.PAS ---------------------------------
  220.  
  221. unit Unit1;
  222.  
  223. interface
  224.  
  225. uses
  226.   SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  227.   Forms, Dialogs, StdCtrls;
  228.  
  229. type
  230.   TForm1 = class(TForm)
  231.     btnCreate: TButton;
  232.     btnSave: TButton;
  233.     btnLoad: TButton;
  234.     btnExit: TButton;
  235.     procedure btnCreateClick(Sender: TObject);
  236.     procedure btnSaveClick(Sender: TObject);
  237.     procedure btnLoadClick(Sender: TObject);
  238.     procedure btnExitClick(Sender: TObject);
  239.   private
  240.     { Private declarations }
  241.   public
  242.     { Public declarations }
  243.   end;
  244.  
  245. var
  246.   Form1: TForm1;
  247.  
  248.  
  249. implementation
  250.  
  251. {$R *.DFM}
  252.  
  253. uses
  254.   Unit2;
  255.  
  256. const
  257.   ObjFilename = 'C:\players.dat';
  258.  
  259. var
  260.   objList : TObjectList;
  261.  
  262. procedure TForm1.btnCreateClick(Sender: TObject);
  263. var
  264.   player : TPlayer;
  265.   i      : integer;
  266. begin
  267.   {Creates five players and adds them to ObjList}
  268.   objList.Clear;
  269.   for i := 1 to 5 do begin
  270.     player := TPlayer.Create;
  271.     try
  272.       with player do begin
  273.         Name       := 'Name' + IntToStr(i);
  274.         EmpireName := 'EmpireName' + IntToStr(i);
  275.         Wins       := i;
  276.         Losses     := 5 - i;
  277.         Ranking    := 6 - i;
  278.       end;
  279.       objList.Add(player);
  280.     except
  281.       player.Free;
  282.       raise;
  283.     end;
  284.   end;
  285. end;
  286.  
  287. procedure TForm1.btnSaveClick(Sender: TObject);
  288. begin
  289.   {Stores objList to file}
  290.   objList.SaveToFile(ObjFilename);
  291. end;
  292.  
  293. procedure TForm1.btnLoadClick(Sender: TObject);
  294. begin
  295.   {Loads objList from file}
  296.   objList.LoadFromFile(ObjFilename);
  297. end;
  298.  
  299. procedure TForm1.btnExitClick(Sender: TObject);
  300. begin
  301.   {Frees objList (and everything in list) and exits}
  302.   objList.Free;
  303.   Close;
  304. end;
  305.  
  306. initialization
  307.   objList := TObjectList.Create;
  308. end.
  309.  
  310. --------------------------------- UNIT2.PAS ---------------------------------
  311.  
  312. unit Unit2;
  313.  
  314. interface
  315.  
  316. uses
  317.   Classes;
  318.  
  319. type
  320.   TPlayer = class(TPersistent)
  321.   private
  322.     FName       : string;
  323.     FEmpireName : string;
  324.     FWins       : integer;
  325.     FLosses     : integer;
  326.     FRanking    : integer;
  327.   public
  328.     procedure ReadData(reader : TReader); dynamic;
  329.     procedure WriteData(writer : TWriter); dynamic;
  330.   published
  331.     property Name : string read FName write FName;
  332.     property EmpireName : string read FEmpireName write FEmpireName;
  333.     property Wins : integer read FWins write FWins;
  334.     property Losses : integer read FLosses write FLosses;
  335.     property Ranking : integer read FRanking write FRanking;
  336.   end;
  337.  
  338.   TObjectlist=class(TList)
  339.   public
  340.     destructor Destroy; override;
  341.     procedure Clear;
  342.     procedure SaveToStream(stream : TStream);
  343.     procedure LoadFromStream(stream : TStream);
  344.     procedure SaveToFile(const filename : string);
  345.     procedure LoadFromFile(const filename : string);
  346.   end;
  347.  
  348.  
  349. implementation
  350.  
  351. uses
  352.   SysUtils;
  353.  
  354. {TPlayer}
  355.  
  356. procedure TPlayer.ReadData(reader : TReader);
  357. begin
  358.   with reader do begin
  359.     Name       := ReadString;
  360.     EmpireName := ReadString;
  361.     Wins       := ReadInteger;
  362.     Losses     := ReadInteger;
  363.     Ranking    := ReadInteger;
  364.   end;
  365. end;
  366.  
  367. procedure TPlayer.WriteData(writer : Twriter);
  368. begin
  369.   with writer do begin
  370.     WriteString(Name);
  371.     WriteString(EmpireName);
  372.     WriteInteger(Wins);
  373.     WriteInteger(Losses);
  374.     WriteInteger(Ranking);
  375.   end;
  376. end;
  377.  
  378.  
  379. {TObjectList}
  380.  
  381. destructor TObjectList.Destroy;
  382. begin
  383.   {deallocate objects in list before termination}
  384.   Clear;
  385.   inherited Destroy;
  386. end;
  387.  
  388. procedure TObjectList.Clear;
  389. var
  390.   i : integer;
  391. begin
  392.   {This routine deallocates all resources inside this list}
  393.   for i := 0 to Count - 1 do begin
  394.     TObject(Items[0]).Free;
  395.     Delete(0);
  396.   end;
  397. end;
  398.  
  399. procedure TObjectList.SaveToStream(stream : TStream);
  400. var
  401.   writer : TWriter;
  402.   i      : integer;
  403. begin
  404.     writer := TWriter.Create(stream, $ff);
  405.     try
  406.       with writer do begin
  407.         {mark beginning of file and beginning of object list}
  408.         WriteSignature;
  409.         WriteListBegin;
  410.         {loop through this list}
  411.         for i := 0 to Count - 1 do begin
  412.           {Store any TPersistent objects}
  413.           if TObject(Items[i]) is TPersistent then begin
  414.             WriteString(TPersistent(Items[i]).ClassName);
  415.             {Call WriteData() for TPlayer objects}
  416.             if (TPersistent(Items[i]) is TPlayer) then
  417.               TPlayer(Items[i]).WriteData(writer);
  418.           end;
  419.         end;
  420.         {mark end of object list}
  421.         WriteListEnd;
  422.       end;
  423.     finally
  424.       writer.Free;
  425.     end;
  426. end;
  427.  
  428. procedure TObjectList.LoadFromStream(stream : TStream);
  429. var
  430.   reader : TReader;
  431.   obj    : TPersistent;
  432.   ctype  : TPersistentClass;
  433.   cname  : string;
  434.   i      : integer;
  435. begin
  436.   reader:=TReader.Create(stream,$ff);
  437.   try
  438.     with reader do begin
  439.       {read beginning of file and beginning of object list markers}
  440.       ReadSignature;
  441.       ReadListBegin;
  442.       {loop through file list of objects}
  443.       while not EndOfList do begin
  444.         {Load ClassName and use it to get ClassType}
  445.         cname := ReadString;
  446.         ctype := GetClass(cname);
  447.         if Assigned(ctype) then begin  {"Assigned()" == " <> nil" but quicker}
  448.           {If a ClassType was found, create an instance}
  449.           obj := ctype.Create;
  450.           try
  451.             {if obj is a TPlayer, call its ReadData() method}
  452.             if obj is TPlayer then
  453.               TPlayer(obj).ReadData(reader);
  454.           except
  455.             obj.free;
  456.             raise;
  457.           end;
  458.           {add object to this list}
  459.           Add(obj);
  460.         end;
  461.       end;
  462.       ReadListEnd;
  463.     end;
  464.   finally
  465.     reader.Free;
  466.   end;
  467. end;
  468.  
  469. procedure TObjectList.SaveToFile(const filename : string);
  470. var
  471.   stream : TFileStream;
  472. begin
  473.   stream := TFileStream.Create(filename, fmCreate or fmOpenWrite);
  474.   try
  475.     SaveToStream(stream);
  476.   finally
  477.     stream.Free;
  478.   end;
  479. end;
  480.  
  481. procedure TObjectList.LoadFromFile(const filename : string);
  482. var
  483.   stream : TFileStream;
  484. begin
  485.   stream := TFileStream.Create(filename, fmOpenRead);
  486.   try
  487.     Clear;
  488.     LoadFromStream(stream);
  489.   finally
  490.     stream.Free;
  491.   end;
  492. end;
  493.  
  494. initialization
  495.   {register TPlayer class here when program begins}
  496.   RegisterClass(TPlayer);
  497. end.
  498.